/** @file gui_profiler.h
    @brief A performance profiler stats page */

#ifndef G3_PROFILER_H
#define G3_PROFILER_H

#include "log.h"

#include "color.h"
#include "text.h"
#include "timer.h"

#include "gui/gui.h"
#include "gui/gui_stats.h"

#include <string>
#include <list>
#include <map>

namespace G3 {
namespace GUI {

/** @class PROFILER_ENTRY
    @brief A performance profiler stats page entry class */
class PROFILER_ENTRY: public STATS_ENTRY {
    /** The profile manager class is out friend - well, at least we think so =P */
    friend class PROFILER_MANAGER;

	public:
        /**
            @brief A multifunctional constructor
            @param[in] aName Reference to the name of this profiler page
            @param[in] aTimeAccum Already accumulated time
            @param[in] aDepth Depth of the entry in the profiler tree
            \todo Review these parameters and try to remember what they did
        */
		PROFILER_ENTRY (const std::string &aName, const long &aTimeAccum = 0, const int &aDepth = 0);
		/**
            @brief A destructor
		*/
		~PROFILER_ENTRY ();

        /** The label for this entry */
		LABEL Line;

        /** A list of time-spent measurements */
		std::list<long> TimeSpent;
		/** A list of calculated percentages */
		std::list<float> Percentages;
		/** Index of the current sample */
		int CurrentSample;

        /** Minimum from the time-spent measurements */
		long MinimumTimeSpent;
		/** Average of the time-spent measurements */
		long AverageTimeSpent;
		/** Maximum of the time-spent measurements */
		long MaximumTimeSpent;
		/** Minimum of the calculated percentages */
		float MinPercentage;
		/** Average of the calculated percentages */
		float AvgPercentage;
		/** Maximum of the calculated percentages */
		float MaxPercentage;

        /**
            @brief Attaches a font to the entry - needed for integration into Stats
            @param[in] aFont pointer to the new font
        */
		void AttachFont (FONT *aFont);

        /**
            @brief Adds a sample to this entry
            @param[in] aTimeSpent Reference to the amount of milliseconds spent on some process
        */
		void AddSample (const long &aTimeSpent);

        /**
            @brief Gets the total time spent on the process
            @return The total time
        */
		long TotalTimeSpent ();
		/**
            @brief Gets the minimum time spent on the process
            @return The minimum time
		*/
		long MinTimeSpent ();
		/**
            @brief Gets the maximum time spent on the process
            @return The maximum time
		*/
		long MaxTimeSpent ();

        /**
            @brief Clears the profiler entry
        */
		void Clear ();

        /**
            @brief Draws an entry - needed for integration into Stats
            @param[in] Position Reference to the drawing position on the screen
            @param[in] Highlighted Is this entry highlighted ?
            @param[in] aElapsedTime The elapsed time since the last call
        */
		void Draw (const VEC2I &Position, bool Highlighted = false, float aElapsedTime = 0.0f);

	private:
        /** Total accumulated time */
		long TimeAccum;
		/** The depth of this entry in the profiler tree */
		int Depth;

        /**
            @brief Calculates Min, Avg, Max percentages
            @param[in] aTotalTime Reference to the total time spent on this profiler branch
        */
		void CalcEntryPercentage (const long &aTotalTime);

        /**
            @brief Calculates Min, Avg, Max time spent on the scope
        */
		void CalcEntryTimings ();
};

/** @class PROFILER_MANAGER
    @brief A profiler manager singleton class */
class PROFILER_MANAGER: public SINGLETON<PROFILER_MANAGER> {
    /** The profiler class is a dear friend of ours */
    friend class PROFILER;

	public:
        /**
            @brief A constructor
        */
		PROFILER_MANAGER ();
        /**
            @brief Yup, it's a destructor
        */
		~PROFILER_MANAGER ();

        /** Is profiling enabled ? \note Profiling is slow and should only be used for debugging */
		bool profilingEnabled;

        /** Total time spent on the root */
		long TotalTime;
		/** Number of maximum samples. One more and the oldest gets overwritten */
		int NumMaxSamples;
		/** Profiler entries - accessible by identifier strings */
		std::map<std::string, PROFILER_ENTRY *> Entries;
        /** Pointer to the log */
		G3::LOG *log;

        /**
            @brief Adds the profiler information to a Stats page
            @param[in] aFont Pointer to the font to be used
            @param[in] aRect Reference to the profiler group rectangle
        */
		void AddStatsPage (FONT *aFont, const RECTI &aRect);

	private:
        /** Pointer to the parent stats page */
		STATS_PAGE *StatsPage;
		/** Current depth of the profiler tree */
		int Depth;

        /**
            @brief Marks the start of a profiled scope
        */
		void AddDepth ();

        /**
            @brief Adds a sample to the profiler manager
            @param[in] aName Reference to the scope identifier
            @param[in] aTicks Time spent on the scope
        */
		void AddSample (const std::string &aName, const long &aTicks);
};

/** The main instance of the profiler manager singleton */
extern PROFILER_MANAGER MainProfilerMgr;

/** @class PROFILER
    @brief A profiler class - this measures the speed and usage of a scope */
class PROFILER {
	public:
        /**
            @brief A constructor that starts measuring
            @param[in] aName Reference to the scope identifier string
        */
		PROFILER (const std::string &aName);
		/**
            @brief A destructor that uploads the measurements to the profiler manager
		*/
		~PROFILER ();

	private:
        /** Scope identifier */
		std::string Name;
		/** The timer that takes the measurements */
		TIMER Timer;
};

}
}

#endif //G3_PROFILER_H
